﻿using System;
using System.Drawing;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Windows.Input;
using Foundation;
using ICSharpCode.AvalonEdit;
using Poet.Commands;
using SaveFileDialog = Microsoft.Win32.SaveFileDialog;
using Poet.Views;

namespace Poet.ViewModels
{
    internal class DocumentViewModel : BaseMainViewModel
    {
        static DocumentViewModel()
        {
            GoTo = new RoutedCommand(
                "GoToLine",
                typeof(DocumentViewModel),
                new InputGestureCollection { new KeyGesture(Key.G, ModifierKeys.Control) });
        }

        public DocumentViewModel(string fileName = null)
        {
            Title = string.Format("New{0}", ++NewDocumentCounter);
            TextEditor = new TextEditor { ShowLineNumbers = true };

            ChangeFont = new Command(ChangeFontExecuted);

            CommandBindings.Add(new CommandBinding(GoTo, GoToLineExecuted));
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Open, OnOpenExecute));
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Close, OnCloseExecute));
            CommandBindings.Add(new CommandBinding(ApplicationCommands.SaveAs, OnSaveAsExecute));
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Save, OnSaveExecute, (o, e) => e.CanExecute = TextEditor.IsModified));
            CommandBindings.Add(new CommandBinding(SearchAndReplaceCommands.FindPrev, Executed, CanExecuted));
            CommandBindings.Add(new CommandBinding(SearchAndReplaceCommands.FindNext, Executed, CanExecuted));
            CommandBindings.Add(new CommandBinding(SearchAndReplaceCommands.ReplacePrev, Executed, CanExecuted));
            CommandBindings.Add(new CommandBinding(SearchAndReplaceCommands.ReplaceNext, Executed, CanExecuted));

            if (fileName != null)
            {
                OpenFile(fileName);
            }
        }

        private void OnOpenExecute(object sender, ExecutedRoutedEventArgs args)
        {
            OpenFile((string)args.Parameter);
        }

        private void OpenFile(string fileName)
        {
            try
            {
                Load(File.Open(fileName, FileMode.Open));
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
        }

        public static ICommand GoTo { get; set; }

        public static Font DefaultFont { get; set; }

        public ICommand ChangeFont { get; set; }

        public string Title { get; set; }

        public string FullName { get; set; }

        public string ShortName { get; set; }

        public TextEditor TextEditor { get; set; }

        public Stream DataStream { get; set; }

        public EventHandler OnClosed;

        private static int NewDocumentCounter { get; set; }

        private readonly SaveFileDialog _saveFileDialog = new SaveFileDialog();

        /// <summary>
        /// Loads the specified stream.
        /// </summary>
        /// <param name="stream">The stream.</param>
        public void Load(Stream stream)
        {
            DataStream = stream;
            TextEditor.Load(DataStream);
        }

        private void SetFont(Font font)
        {
            TextEditor.FontFamily = new System.Windows.Media.FontFamily(font.FontFamily.Name);
            TextEditor.FontSize = font.Size;
        }

        private void CanExecuted(object sender, CanExecuteRoutedEventArgs args)
        {
            var searchViewModel = (SearchViewModel)args.Parameter;
            var isSearch = args.Command == SearchAndReplaceCommands.FindNext
                           || args.Command == SearchAndReplaceCommands.FindPrev;

            args.CanExecute = isSearch
                                  ? Search(searchViewModel, args.Command, false)
                                  : Replace(searchViewModel, args.Command, false)
                                    || Search(searchViewModel, args.Command, false);
        }

        private void Executed(object sender, ExecutedRoutedEventArgs args)
        {
            var searchViewModel = (SearchViewModel)args.Parameter;
            var isSearch = args.Command == SearchAndReplaceCommands.FindNext
                           || args.Command == SearchAndReplaceCommands.FindPrev;
            if (isSearch || !Replace(searchViewModel, args.Command, true)) Search(searchViewModel, args.Command, true);
        }

        private void GoToLineExecuted(object parameter, ExecutedRoutedEventArgs executedRoutedEventArgs)
        {
            var view = new GoToLineView { Topmost = false };
            var viewModel = new GoToLineViewModel
                { MinLineNumber = 1, MaxLineNumber = TextEditor.LineCount, LineNumber = TextEditor.TextArea.Caret.Line };

            viewModel.Bind(view);

            if (view.ShowDialog() == true)
            {
                TextEditor.TextArea.Caret.Line = viewModel.LineNumber;
            }
        }

        private void ChangeFontExecuted(object parameter)
        {
            var fontDialog = new FontDialog
                { Font = new Font(TextEditor.FontFamily.ToString(), (float)TextEditor.FontSize) };

            if (fontDialog.ShowDialog() != DialogResult.OK)
            {
                TextEditor.Focus();
                return;
            }

            DefaultFont = fontDialog.Font;
            SetFont(DefaultFont);
            TextEditor.Focus();
        }

        private void OnSaveAsExecute(object sender, ExecutedRoutedEventArgs e)
        {
            if (_saveFileDialog.ShowDialog() == true)
            {
                DataStream = _saveFileDialog.OpenFile();
                TextEditor.Save(DataStream);
            }
        }

        private void OnSaveExecute(object sender, ExecutedRoutedEventArgs e)
        {
            if (DataStream != null)
            {
                TextEditor.Save(DataStream);
            }
            else
            {
                OnSaveAsExecute(sender, e);
            }
        }

        private void OnCloseExecute(object sender, ExecutedRoutedEventArgs e)
        {
            if (DataStream != null)
            {
                DataStream.Close();
            }

            OnClosed(sender, e);
        }

        private bool Search(SearchViewModel searchViewModel, ICommand command, bool selectFoundedSubstring)
        {
            var directSearch = SearchAndReplaceCommands.FindNext == command
                               || SearchAndReplaceCommands.ReplaceNext == command;
            var options = searchViewModel.Options;
            var pattern = options.UseRegularExpression ? searchViewModel.RegexPattern : searchViewModel.TextPattern;

            if (string.IsNullOrEmpty(pattern))
            {
                return false;
            }

            var text = TextEditor.Text;
            var matchCase = options.MatchCase;
            var matchWholeWord = options.MatchWholeWord;
            var startIndex = searchViewModel.ActiveDocumentView.DataContext != this
                                 ? directSearch ? 0 : TextEditor.Text.Length
                                 : directSearch ? TextEditor.CaretOffset : TextEditor.SelectionStart;

            if (options.UseRegularExpression)
            {
                var match = text.SearchByRegex(pattern, startIndex, directSearch, matchCase, matchWholeWord);

                if (!string.IsNullOrEmpty(match.ToString()))
                {
                    if (selectFoundedSubstring) SelectFoundedSubstring(match.Index, match.Length);

                    return true;
                }
            }
            else
            {
                startIndex = text.SearchByPattern(pattern, startIndex, directSearch, matchCase, matchWholeWord);

                if (0 <= startIndex && startIndex < text.Length)
                {
                    if (selectFoundedSubstring) SelectFoundedSubstring(startIndex, pattern.Length);

                    return true;
                }
            }

            return false;
        }

        private void SelectFoundedSubstring(int startIndex, int length)
        {
            TextEditor.Select(startIndex, length);
            TextEditor.ScrollToLine(TextEditor.TextArea.Caret.Line);
            TextEditor.ScrollToHorizontalOffset(TextEditor.TextArea.Caret.Offset);
        }

        private bool Replace(SearchViewModel searchViewModel, ICommand command, bool doReplace)
        {
            if (string.IsNullOrEmpty(TextEditor.SelectedText)) return false;

            bool selectedTextIsMatch;
            if (searchViewModel.Options.UseRegularExpression) selectedTextIsMatch = new Regex(searchViewModel.RegexPattern).IsMatch(TextEditor.SelectedText);
            else selectedTextIsMatch = TextEditor.SelectedText == searchViewModel.TextPattern;

            if (doReplace)
            {
                if (string.IsNullOrEmpty(TextEditor.SelectedText) && selectedTextIsMatch)
                {
                    return Search(searchViewModel, command, true);
                }

                TextEditor.SelectedText = searchViewModel.RegexPattern;
                return Search(searchViewModel, command, true);
            }

            return searchViewModel.TextPattern == TextEditor.SelectedText;
        }
    }
}
